package edu.cuit.zhuyimeng.flight.service.impl;

import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheInvalidate;
import com.baomidou.dynamic.datasource.annotation.Master;
import edu.cuit.zhuyimeng.aircraft.api.controller.AircraftUpdateApiController;
import edu.cuit.zhuyimeng.flight.controller.dto.QueryFlightDTO;
import edu.cuit.zhuyimeng.flight.controller.vo.FlightInfoVO;
import edu.cuit.zhuyimeng.flight.convert.FlightConverter;
import edu.cuit.zhuyimeng.flight.dal.po.FlightPO;
import edu.cuit.zhuyimeng.flight.enums.CacheConstants;
import edu.cuit.zhuyimeng.flight.service.IFlightDispatchService;
import edu.cuit.zhuyimeng.flight.service.IFlightQueryService;
import edu.cuit.zhuyimeng.flight.service.IJobDispatchService;
import edu.cuit.zhuyimeng.framework.cache.aspect.annotation.CacheInvalidatePattern;
import edu.cuit.zhuyimeng.framework.common.exception.UpdateException;
import edu.cuit.zhuyimeng.framework.common.system.enums.FlightStatus;
import edu.cuit.zhuyimeng.framework.common.system.enums.SysConstants;
import edu.cuit.zhuyimeng.framework.common.util.TimeUtils;
import edu.cuit.zhuyimeng.framework.logging.aspect.aop.OperateLogAspect;
import edu.cuit.zhuyimeng.framework.workflow.util.ProcessUtils;
import edu.cuit.zhuyimeng.ticket.api.controller.TicketDispatchApiController;
import lombok.RequiredArgsConstructor;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import tech.powerjob.common.response.ResultDTO;

import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 航班调度服务实现类
 */
@Service
@RequiredArgsConstructor
public class FlightDispatchService implements IFlightDispatchService {

    private final RuntimeService runtimeService;
    private final TaskService taskService;

    private final Cache<String, List<Long>> specificFlightInfoCache;
    private final Cache<Integer,List<Long>> paginationFlightInfoCache;
    private final Cache<Long, FlightInfoVO> flightInfoCache;

    private final AircraftUpdateApiController aircraftUpdateApiController;
    private final TicketDispatchApiController ticketDispatchApiController;

    private final IFlightQueryService flightQueryService;
    private final IJobDispatchService jobDispatchService;

    @Override
    @CacheInvalidate(name = CacheConstants.FLIGHT_INFO,key = "args[0]")
    @Master
    public void takeoff(Long flightId) {
        FlightInfoVO flightInfoVO = flightQueryService.queryOneFlight(flightId);
        if (flightInfoVO == null) {
            throw new UpdateException("此航班不存在");
        }
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(String.valueOf(flightInfoVO.getId()))
                .includeProcessVariables()
                .singleResult();
        Map<String, Object> processVariables = processInstance.getProcessVariables();
        if (!Objects.equals(processVariables.get(SysConstants.STATUS), FlightStatus.WAITING_TAKEOFF.getType())) {
            throw new UpdateException("当前航班不是等待起飞的状态");
        }

        Task task = taskService.createTaskQuery().processInstanceBusinessKey(String.valueOf(flightId)).singleResult();
        taskService.complete(task.getId());

        processVariables.put(SysConstants.STATUS,FlightStatus.FLYING.getType());
        ProcessUtils.setVariables(processInstance,processVariables);
        specificFlightInfoCache.remove(QueryFlightDTO.builder()
                .flightDate(TimeUtils.gerLocalData(flightInfoVO.getStartTime()))
                .deployCity(flightInfoVO.getDeployCity())
                .destCity(flightInfoVO.getArrivalCity()).build().getCacheKey());
        ticketDispatchApiController.refreshCache(flightId);
    }

    @Override
    @CacheInvalidate(name = CacheConstants.FLIGHT_INFO,key = "args[0]")
    @Master
    public void landing(Long flightId) {
        FlightInfoVO flightInfoVO = flightQueryService.queryOneFlight(flightId);
        if (flightInfoVO == null) {
            throw new UpdateException("此航班不存在");
        }
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(String.valueOf(flightInfoVO.getId()))
                .includeProcessVariables()
                .singleResult();
        Map<String, Object> processVariables = processInstance.getProcessVariables();
        if (!Objects.equals(processVariables.get(SysConstants.STATUS), FlightStatus.FLYING.getType())) {
            throw new UpdateException("当前航班不是飞行中的状态");
        }

        Task task = taskService.createTaskQuery().processInstanceBusinessKey(String.valueOf(flightId)).singleResult();
        taskService.complete(task.getId());

        ResultDTO<Long> longResultDTO = jobDispatchService.startDestroyFlightJob(flightId);
        processVariables.put(SysConstants.STATUS,FlightStatus.ENDED.getType());
        processVariables.put(SysConstants.JOB_ID_KEY,longResultDTO.getData());
        ProcessUtils.setVariables(processInstance,processVariables);
        ticketDispatchApiController.refreshCache(flightId);
    }

    @Override
    @CacheInvalidatePattern(name = CacheConstants.PAGE_FLIGHT_INFO,pattern = "*")
    @CacheInvalidate(name = CacheConstants.FLIGHT_AMOUNT,key = "'all'")
    @Master
    public void initFlight(FlightPO flightPO) {
        flightInfoCache.put(flightPO.getId(), FlightConverter.convertToFlightInfoVO(flightPO));
        LocalDate localDate = TimeUtils.gerLocalData(flightPO.getStartTime());
        specificFlightInfoCache.remove(QueryFlightDTO.builder()
                .flightDate(localDate)
                .deployCity(flightPO.getDeployCity())
                .destCity(flightPO.getArrivalCity()).build().getCacheKey());
        //锁定飞机
        aircraftUpdateApiController.lockAircraft(flightPO.getAircraftId());

        Map<String, Object> variables = new HashMap<>();
        variables.put(SysConstants.STATUS, FlightStatus.PREPARING.getType());
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey(SysConstants.PROCESS_ID, String.valueOf(flightPO.getId()), variables);

        OperateLogAspect.setContent("航班工作流流程启动，id: " + processInstance.getId());
        OperateLogAspect.addExtends("航班号",flightPO.getNumber());
    }

    @Override
    @CacheInvalidate(name = CacheConstants.FLIGHT_INFO,key = "args[0]")
    @Master
    public void nextPhase(Long id) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(String.valueOf(id))
                .includeProcessVariables()
                .singleResult();
        FlightStatus flightStatus = FlightStatus.getFlightStatusByType((Integer) processInstance.getProcessVariables().get(SysConstants.STATUS));
        assert flightStatus != null;
        FlightStatus nextFlightStatus = flightStatus.nextPhase();
        if (nextFlightStatus == null) {
            throw new UpdateException("当前已经是最后一个阶段了");
        }
        Map<String, Object> processVariables = processInstance.getProcessVariables();
        processVariables.put(SysConstants.STATUS,nextFlightStatus.getType());
        ProcessUtils.setVariables(processInstance,processVariables);
    }

    @Override
    @CacheInvalidatePattern(name = CacheConstants.PAGE_FLIGHT_INFO,pattern = "*")
    @CacheInvalidate(name = CacheConstants.FLIGHT_AMOUNT,key = "'all'")
    @Master
    public void destroyFlight(Long id) {
        FlightInfoVO flightInfoVO = flightQueryService.queryOneFlight(id);
        LocalDate localDate = TimeUtils.gerLocalData(flightInfoVO.getStartTime());
        specificFlightInfoCache.remove(QueryFlightDTO.builder()
                .flightDate(localDate)
                .deployCity(flightInfoVO.getDeployCity())
                .destCity(flightInfoVO.getArrivalCity()).build().getCacheKey());
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(String.valueOf(id)).singleResult();
        runtimeService.deleteProcessInstance(processInstance.getProcessInstanceId(),"航班销毁");
        aircraftUpdateApiController.unlockAircraft(flightInfoVO.getAircraftInfoDTO().getId());
    }

}
